home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue45 / System / EXBitmap.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-11-02  |  10.8 KB  |  348 lines

  1. unit EXBitmap;
  2.  
  3. interface
  4.  
  5. uses
  6.     Windows, Messages, SysUtils, Classes, Graphics, Controls, Dialogs;
  7.  
  8. type
  9.     TExBrightness = -255..255;
  10.  
  11.     TExBitmap = class (TBitmap)
  12.     private
  13.         fChangeLock: Boolean;                    // True if we're altering image
  14.         fOriginal: TBitmap;                      // original bitmap image
  15.         fBrightness: TExBrightness;              // current brightness level
  16.         fFlipped: Boolean;                       // if image is vertically flipped
  17.         fMirrored: Boolean;                      // if image is horizontally mirrored
  18.         fInverted: Boolean;                      // if image is inverted (negative)
  19.         fBlurRadius: Double;                     // radius for Gaussian blur
  20.         fWeights: array [-100..100] of single;
  21.         fSize: 1..100;
  22.         procedure SetBrightness (Value: TExBrightness);
  23.         procedure SetMirrored (Value: Boolean);
  24.         procedure SetInverted (Value: Boolean);
  25.         procedure SetFlipped (Value: Boolean);
  26.         procedure SetBlurRadius (Value: Double);
  27.         procedure InitGaussianWeightings;
  28.         procedure BlurRow (S, D: Pointer; Count: Integer);
  29.     protected
  30.         procedure Changed (Sender: TObject); override;
  31.     public
  32.         constructor Create; override;
  33.         destructor Destroy; override;
  34.         procedure GaussianBlur;
  35.         property Flipped: Boolean read fFlipped write SetFlipped default False;
  36.         property Mirrored: Boolean read fMirrored write SetMirrored default False;
  37.         property Brightness: TExBrightness read fBrightness write SetBrightness default 0;
  38.         property Inverted: Boolean read fInverted write SetInverted default False;
  39.         property BlurRadius: Double read fBlurRadius write SetBlurRadius;
  40.     end;
  41.  
  42. implementation
  43.  
  44. type
  45.     // Override the definitions in Windows.pas
  46.     PRGBQuad = ^TRGBQuad;
  47.     TRGBQuad = packed record
  48.         r: Byte;
  49.         g: Byte;
  50.         b: Byte;
  51.         rgbReserved: Byte;
  52.     end;
  53.  
  54.     // Override the definitions in Graphics.pas
  55.     TRGBQuadArray = array [Word] of TRGBQuad;
  56.     PRGBQuadArray = ^TRGBQuadArray;
  57.  
  58. { TExBitmap }
  59.  
  60. constructor TExBitmap.Create;
  61. begin
  62.     Inherited Create;
  63.     fBlurRadius := 3.0;
  64.     fOriginal := TBitmap.Create;
  65. end;
  66.  
  67. destructor TExBitmap.Destroy;
  68. begin
  69.     fOriginal.Free;
  70.     Inherited Destroy;
  71. end;
  72.  
  73. procedure TExBitmap.Changed (Sender: TObject);
  74. begin
  75.     Inherited Changed (Sender);
  76.     if not fChangeLock then begin
  77.         PixelFormat := pf32Bit;
  78.         fBrightness := 0;
  79.         fFlipped := False;
  80.         fMirrored := False;
  81.         fInverted := False;
  82.         // Force a *COPY* of the bitmap.  >>DONT<< call Assign!
  83.         fOriginal.Width := Width;  fOriginal.Height := Height;
  84.         fOriginal.Canvas.Draw (0, 0, Self);
  85.         fOriginal.PixelFormat := pf32Bit;
  86.     end;
  87. end;
  88.  
  89. procedure TExBitmap.SetBrightness (Value: TExBrightness);
  90. var
  91.     Row, Col: Integer;
  92.     Line: PRGBQuadArray;
  93. begin
  94.     if (not Empty) and (fBrightness <> Value) then begin
  95.         fBrightness := Value;
  96.         fChangeLock := True;
  97.         // Get an unadulterated copy of the image
  98.         Canvas.Draw (0, 0, fOriginal);
  99.         Assert (PixelFormat = pf32Bit);
  100.         for Row := 0 to Height - 1 do begin
  101.             Line := ScanLine [Row];
  102.             for Col := 0 to Width - 1 do
  103.                 with Line [Col] do
  104.                     if Value > 0 then begin
  105.                         if r + Value > 255 then r := 255 else Inc (r, Value);
  106.                         if g + Value > 255 then g := 255 else Inc (g, Value);
  107.                         if b + Value > 255 then b := 255 else Inc (b, Value);
  108.                     end else begin
  109.                         if r + Value < 0 then r := 0 else Inc (r, Value);
  110.                         if g + Value < 0 then g := 0 else Inc (g, Value);
  111.                         if b + Value < 0 then b := 0 else Inc (b, Value);
  112.                     end;
  113.         end;
  114.  
  115.         fChangeLock := False;
  116.         Inherited Changed (Self);
  117.     end;
  118. end;
  119.  
  120. procedure TExBitmap.SetFlipped (Value: Boolean);
  121.  
  122.     procedure FlipBitmap (bmp: TBitmap);
  123.     var
  124.         TempScanLine: Pointer;
  125.         ScanLineBytes, Row, H: Integer;
  126.     begin
  127.         with bmp do begin
  128.             Assert (PixelFormat = pf32Bit);
  129.             H := Height;
  130.             ScanLineBytes := Width * sizeof (TRGBQuad);
  131.             GetMem (TempScanLine, ScanLineBytes);
  132.  
  133.             for Row := 0 to ((H and (-2)) - 1) div 2 do begin
  134.                 Move (ScanLine [Row]^, TempScanLine^, ScanLineBytes);
  135.                 Move (ScanLine [H - Row - 1]^, ScanLine [Row]^, ScanLineBytes);
  136.                 Move (TempScanLine^, ScanLine [H - Row - 1]^, ScanLineBytes);
  137.             end;
  138.         end;
  139.  
  140.         FreeMem (TempScanLine);
  141.     end;
  142.  
  143. begin
  144.     if (not Empty) and (Value <> fFlipped) then begin
  145.         fFlipped := Value;
  146.         FlipBitmap (Self);
  147.         // Lossless operation - so apply to original also.
  148.         FlipBitmap (fOriginal);
  149.         Inherited Changed (Self);
  150.     end;
  151. end;
  152.  
  153. procedure TExBitmap.SetMirrored (Value: Boolean);
  154.  
  155.     procedure MirrorBitmap (bmp: TBitmap);
  156.     var
  157.         Temp: TRGBQuad;
  158.         Row, Col, W: Integer;
  159.         Line: PRGBQuadArray;
  160.     begin
  161.         with bmp do begin
  162.             Assert (PixelFormat = pf32Bit);
  163.             W := Width;
  164.             for Row := 0 to Height - 1 do begin
  165.                 Line := ScanLine [Row];
  166.                 for Col := 0 to ((W and (-2)) - 1) div 2 do begin
  167.                     Temp := Line [Col];
  168.                     Line [Col] := Line [W - Col - 1];
  169.                     Line [W - Col - 1] := Temp;
  170.                 end;
  171.             end;
  172.         end;
  173.     end;
  174.  
  175. begin
  176.     if (not Empty) and (Value <> fMirrored) then begin
  177.         fMirrored := Value;
  178.         MirrorBitmap (Self);
  179.         // Lossless operation - so apply to original also.
  180.         MirrorBitmap (fOriginal);
  181.         Inherited Changed (Self);
  182.     end;
  183. end;
  184.  
  185. procedure TExBitmap.SetInverted (Value: Boolean);
  186.  
  187.     procedure InvertBitmap (bmp: TBitmap);
  188.     var
  189.         Row, Col: Integer;
  190.         Line: PRGBQuadArray;
  191.     begin
  192.         with bmp do begin
  193.             Assert (PixelFormat = pf32Bit);
  194.             for Row := 0 to Height - 1 do begin
  195.                 Line := ScanLine [Row];
  196.                 for Col := 0 to Width - 1 do
  197.                     with Line [Col] do begin
  198.                         r := not r;
  199.                         g := not g;
  200.                         b := not b;
  201.                     end;
  202.             end;
  203.         end;
  204.     end;
  205.  
  206. begin
  207.     if (not Empty) and (Value <> fInverted) then begin
  208.         fInverted := Value;
  209.         InvertBitmap (Self);
  210.         // Lossless operation - so apply to original also.
  211.         InvertBitmap (fOriginal);
  212.         Inherited Changed (Self);
  213.     end;
  214. end;
  215.  
  216. procedure TExBitmap.InitGaussianWeightings;
  217. const
  218.     delta: Double = 1.0 / 510;    // Smaller entries are ignored
  219.     LastRadius: Double = 0.0;
  220. var
  221.     Idx: Integer;
  222.     D: Double;
  223.  
  224.     procedure Normalise (Lo, Hi: Integer);
  225.     var
  226.         Total: Double;
  227.         Idx: Integer;
  228.     begin
  229.         Total := 0;
  230.         for Idx := Lo to Hi do Total := Total + fWeights [Idx];
  231.         for Idx := Lo to Hi do fWeights [Idx] := fWeights [Idx] / Total;
  232.     end;
  233.  
  234. begin
  235.     // If same radius as requested last time, nothing to do....
  236.     if fBlurRadius = LastRadius then Exit;
  237.     LastRadius := fBlurRadius;
  238.  
  239.     // Init the weights array with standard deviation = fBlurRadius
  240.     for Idx := Low (fWeights) to High (fWeights) do begin
  241.         D := Idx / fBlurRadius;  fWeights [Idx] := exp (- D * D / 2);
  242.     end;
  243.  
  244.     // Normalise around maximum bounds
  245.     Normalise (Low (fWeights), High (fWeights));
  246.  
  247.     // Discard entries smaller than Delta
  248.     fSize := High (fWeights);  D := 0;
  249.     while (D < delta) and (fSize > 1) do begin
  250.         D := D + 2 * fWeights [fSize];
  251.         Dec (fSize);
  252.     end;
  253.  
  254.     // Normalise again, using new bounds
  255.     Normalise (-fSize, fSize);
  256. end;
  257.  
  258. procedure TExBitmap.BlurRow (S, D: Pointer; Count: Integer);
  259. var
  260.     Idx, Pix, j, n: Integer;
  261.     rr, gg, bb, w: Double;
  262.     Src: PRGBQuadArray absolute S;
  263.     Dest: PRGBQuadArray absolute D;
  264. begin
  265.     for j := 0 to Count - 1 do begin
  266.         rr := 0; gg := 0; bb := 0;
  267.         for n := -fSize to fSize do begin
  268.             w := fWeights [n];  Idx := j - n;
  269.             // Ensure index is pinned between 0..Count-1
  270.             if Idx < 0 then Idx := 0 else if Idx > Count - 1 then Idx := Count - 1;
  271.             with Src [Idx] do begin
  272.                 rr := rr + w * r;
  273.                 gg := gg + w * g;
  274.                 bb := bb + w * b;
  275.             end;
  276.         end;
  277.  
  278.         with Dest [j] do begin
  279.             Pix := Trunc (rr);
  280.             if Pix < 0 then Pix := 0 else if Pix > 255 then Pix := 255;
  281.             r := Pix;
  282.  
  283.             Pix := Trunc (gg);
  284.             if Pix < 0 then Pix := 0 else if Pix > 255 then Pix := 255;
  285.             g := Pix;
  286.  
  287.             Pix := Trunc (bb);
  288.             if Pix < 0 then Pix := 0 else if Pix > 255 then Pix := 255;
  289.             b := Pix;
  290.         end;
  291.     end;
  292. end;
  293.  
  294. procedure TExBitmap.SetBlurRadius (Value: Double);
  295. begin
  296.     if Value > 0.0 then fBlurRadius := Value;
  297. end;
  298.  
  299. procedure TExBitmap.GaussianBlur;
  300. type
  301.     PPRows = ^TPRows;
  302.     TPRows = array[Word] of PRGBQuadArray;
  303. var
  304.     Rows: PPRows;
  305.     Column, Scratch: PRGBQuadArray;
  306.     ScanLineBytes, H, W, Row, Col: Integer;
  307. begin
  308.     if not Empty then begin
  309.         fChangeLock := True;
  310.         // Get a copy of the original image
  311.         Canvas.Draw (0, 0, fOriginal);
  312.         Assert (PixelFormat = pf32Bit);
  313.         H := Height;  W := Width;
  314.         ScanLineBytes := W * sizeof (TRGBQuad);
  315.         InitGaussianWeightings;
  316.         GetMem (Rows, H * sizeof (Pointer));
  317.         GetMem (Column, H * sizeof (TRGBQuad));
  318.  
  319.         // Retrieve the address of each bitmap scanline
  320.         for Row := 0 to H - 1 do Rows [Row]:= Scanline [Row];
  321.  
  322.         // Blur each row
  323.         GetMem (Scratch, ScanLineBytes);
  324.         for Row := 0 to H - 1 do begin
  325.             BlurRow (Rows [Row], Scratch, W);
  326.             Move (Scratch^, Rows [Row]^, W * sizeof (TRGBQuad));
  327.         end;
  328.  
  329.         // Blur each column
  330.         ReallocMem (Scratch, H * sizeof (TRGBQuad));
  331.         for Col := 0 to W - 1 do begin
  332.             // first read the column into a TRow
  333.             for Row := 0 to H - 1 do Column [Row] := Rows [Row][Col];
  334.             BlurRow (Column, Scratch, H);
  335.             // Replace the column in the destination bitmap
  336.             for Row := 0 to H - 1 do Rows [Row][Col] := Scratch [Row];
  337.         end;
  338.  
  339.         FreeMem (Rows);
  340.         FreeMem (Column);
  341.         FreeMem (Scratch);
  342.         fChangeLock := False;
  343.         Inherited Changed (Self);
  344.     end;
  345. end;
  346.  
  347. end.
  348.